#include "ser.h"
#include "stdlib.h"
#include "string.h"

/*Funciones Internas*/
struct ser_suscripcion{
  ser_callback func;
  void *o;
};

static int ser_n_suscripciones = 0;
static struct ser_suscripcion *ser_suscripciones = 0;


static conexion **ser_clientes = 0;
static int ser_n_clientes=0;

static conexion *accept = 0;
static char procesando = 0;


static void ser_publicar(conexion *c, int codigo, void *data, int size){
  int i;
  for(i=0; i<ser_n_suscripciones; i++){
    ser_suscripciones[i].func(c->socket, codigo, data, size, ser_suscripciones[i].o);
  }
} 

static int get_cliente(int cliente, conexion **c){
  int i;
  for(i=0; i<ser_n_clientes; i++){
    if(ser_clientes[i]->socket==cliente){
      *c = ser_clientes[i];
      return 0;
    }
  }
  return 1;
}

/*Funciones Externas */
int ser_suscribir(ser_callback func, void *o){
  ser_n_suscripciones++;
  ser_suscripciones = realloc(ser_suscripciones, sizeof(struct ser_suscripcion)*ser_n_suscripciones);
  ser_suscripciones[ser_n_suscripciones-1].func = func;
  ser_suscripciones[ser_n_suscripciones-1].o = o;
}

int ser_dessuscribir(void *o){
  int i;
  for(i=0; i<ser_n_suscripciones; i++){
    if(ser_suscripciones[i].o==o){
      ser_suscripciones[i].func = ser_suscripciones[ser_n_suscripciones-1].func;
      ser_suscripciones[i].o = ser_suscripciones[ser_n_suscripciones-1].o;
      ser_n_suscripciones--;
      ser_suscripciones = realloc(ser_suscripciones, sizeof(struct ser_suscripcion)*ser_n_suscripciones);
      i--;
    }
  }
}

/*
 *
 */
int ser_status_cliente(int cliente, int *codigo){
  conexion *c;
  if(get_cliente(cliente, &c)==0){
    *codigo = c->status;
  }else{
    return 1; //Cliente no existe
  }
  return 0;
}

int ser_status(int *codigo){
  if(accept==0)
    *codigo=CON_CERRADO;
  else
    *codigo = accept->status;
  return 0;
}

static int ser_procesar_clientes(){
  int i;
  for(i=0; i<ser_n_clientes; i++){
    conexion *c=ser_clientes[i];
    conexion_procesar(c);
  }
}

static int ser_procesar_conexiones(){
  conexion *c = 0;
  conexion_aceptar(accept, &c, ser_publicar);
  while(c!=0){
    c->eliminar = 0;
    ser_n_clientes++;
    ser_clientes=realloc(ser_clientes, sizeof(conexion)*ser_n_clientes);
    ser_clientes[ser_n_clientes-1]=c;
    conexion_aceptar(accept, &c, ser_publicar);
  }
}

static void ser_limpiar_conexiones_eliminadas() {
  int i;
  for(i=0; i<ser_n_clientes; i++){
	if(ser_clientes[i]->eliminar){
	  conexion_desconectar(ser_clientes[i]);
	  conexion_destruir(ser_clientes +i);

	  ser_clientes[i] = ser_clientes[ser_n_clientes-1];
	  ser_n_clientes--;
	  ser_clientes = realloc(ser_clientes, sizeof(conexion*)*ser_n_clientes);
		i--;
	}
  }
}

int ser_procesar(){
  if (!procesando) {
    procesando = 1;
    ser_procesar_conexiones();
    ser_procesar_clientes();
    procesando = 0;
    ser_limpiar_conexiones_eliminadas();
  }
}

int ser_enviar(int cliente, void *datos, int size){
  conexion *c;
  if(get_cliente(cliente, &c)==0){
    conexion_enviar(c, datos, size);
    return 0;
  }
  return 1;//El cliente no existe

}

int ser_iniciar(int puerto){
  conexion_crear(&accept, ser_publicar);
  conexion_escuchar(accept, puerto);
}

int ser_desconectar(int cliente){
  int i;
  for(i=0; i<ser_n_clientes; i++){
    if(ser_clientes[i]->socket==cliente){
      if (procesando)
        ser_clientes[i]->eliminar = 1;
       else {
        conexion_desconectar(ser_clientes[i]);
        conexion_destruir(ser_clientes +i);
        ser_clientes[i] = ser_clientes[ser_n_clientes-1];
        ser_n_clientes--;
        ser_clientes = realloc(ser_clientes, sizeof(conexion*)*ser_n_clientes);
      }
      return 0;
    }
  }
  return 1;
}

int ser_terminar(){
  int i;
  for(i=0; i<ser_n_clientes; i++){
    conexion_desconectar(ser_clientes[i]);
    conexion_destruir(ser_clientes +i);
  }
  ser_n_clientes = 0;
  free(ser_clientes);
  conexion_desconectar(accept);
  conexion_destruir(&accept);
  return 0;
}

int ser_error(char **descripcion){
  *descripcion ="";
}


